/*
 *  LLFileUtils.cpp
 *  LID Library
 *
 *  Created by Alexander Trutchenko on 09/11/11.
 *  Copyright 2011 League of Independent Developers. All rights reserved.
 *
 */

#include "lid_lib/src/file_managment/LLFileUtils.h"

#include "lid_lib/src/service/android/LLJniHelper.h"
#include "lid_lib/src/service/android/LLJniSystemInfo.h"
#include "lid_lib/src/service/LLLog.h"

//#include "support/zip_support/ZipUtils.h"

#include <android/log.h>
#include <jni.h>

#include <stdio.h>
#include <dirent.h>

using namespace std;

namespace lidlib {
/*
static ZipFile *s_pZipFile = NULL;

static ZipFile* zipFile() 
{
	if (s_pZipFile == NULL)
		s_pZipFile = new ZipFile(s_strResourcePath, "assets/");
	return s_pZipFile;
}
*/
/////////////////////////////////////////////////////
// Utility functions

static std::string fullPathAndroid(const std::string& relativePath)
{
	string fullPath = relativePath;

	if (relativePath[0] != '/')
	{
		// read from apk
		fullPath.insert(0, "assets/");
	}

	fullPath = "/" + fullPath;
	fullPath = LLFileUtils::getResourcePath() + fullPath;

	return fullPath;
}

static bool assetsDirectoryEllements(std::vector<std::string>& result, const std::string& fullDirectoryPath, const char* functionName)
{
	LLJniMethodInfo t;			
	
	if (LLJniHelper::getStaticMethodInfo(t, 
				"com/lidlib/lib/LidlibCommon",
				functionName,
				"(Ljava/lang/String;)[Ljava/lang/String;"))
	{
		jstring stringArg = t.env->NewStringUTF(fullDirectoryPath.c_str());
		jobjectArray pStringArray = (jobjectArray)t.env->CallStaticObjectMethod(t.classID, t.methodID, stringArg);
		t.env->DeleteLocalRef(stringArg);
		t.env->DeleteLocalRef(t.classID);
		if (pStringArray == NULL)
			return false;
			
					// Allocates an array of C string.
		jsize lLength = t.env->functions->GetArrayLength(t.env, pStringArray);

		// Fills the C array with a copy of each input Java string.
		int32_t i, j;
		for (i = 0; i < lLength; ++i)
		{
			// Gets the current Java String from the input Java array.
			// Object arrays can be accessed element by element only.
			jstring pString = (jstring)t.env->functions->GetObjectArrayElement(t.env, pStringArray, i);
			if (t.env->ExceptionCheck())
			{
				return false;
			}
			

			// Directly copies the Java String into our new C buffer.
			// This is usually faster than GetStringUTFChars() which
			// requires copying manually.
			const char* lStringTmp = t.env->functions->GetStringUTFChars(t.env, pString, NULL);
			if (lStringTmp == NULL) 
			{
				return false;
			}
			result.push_back(std::string(lStringTmp));
			t.env->ReleaseStringUTFChars(pString, lStringTmp);
			
			// No need to keep a reference to the Java string anymore.
			t.env->DeleteLocalRef(pString);
		}
		return true;
	}

	return false;
}

/////////////////////////////////////////////////////
//
/** LLFileUtils 
* 
*/
/////////////////////////////////////////////////////

std::vector<std::string> LLFileUtils::getFileList(const std::string& fullDirectoryPath)
{
	std::vector<std::string> result;
	
	if (fullDirectoryPath[0] != '/')
	{
		// read from apk
		assetsDirectoryEllements(result, fullDirectoryPath, "getAssetsFolderFiles");
		return result;
	}
	else
	{
		std::string fingStr = fullPathAndroid(fullDirectoryPath);

		DIR *dp = opendir(fingStr.c_str());
		if (dp == NULL)
		{
			LLLog("LLFileUtils::getDirectoryList(): No directory %s", fingStr.c_str());
			return result;
		}
		struct dirent *dent;
		while(dent = readdir(dp))
			if (strcmp(".", dent->d_name) && 
				strcmp("..", dent->d_name) && 
				dent->d_type != DT_DIR)
				result.push_back(dent->d_name);

		closedir(dp);	
	}

	return result;
}

std::vector<std::string> LLFileUtils::getDirectoryList(const std::string& fullDirectoryPath)
{
	std::vector<std::string> result;
	
	if (fullDirectoryPath[0] != '/')
	{
		// read from apk
		assetsDirectoryEllements(result, fullDirectoryPath, "getAssetsFolderDirectories");
		return result;
	}
	else
	{
		std::string fingStr = fullPathAndroid(fullDirectoryPath);
		std::string str2 = fingStr + "/eng/dictionary.plist";
		FILE* file = fopen(str2.c_str(),"rt");
		if (file != NULL)
		{
			fclose(file);
		}
		else
		{
		}

		DIR *dp = opendir(fingStr.c_str());
		if (dp == NULL)
		{
			LLLog("LLFileUtils::getDirectoryList(): No directory %s", fingStr.c_str());
			return result;
		}
		struct dirent *dent;
		while(dent = readdir(dp))
			if (strcmp(".", dent->d_name) && 
				strcmp("..", dent->d_name) && 
				dent->d_type == DT_DIR)
				result.push_back(dent->d_name);

		closedir(dp);	
	}	
	
	return result;
}

string LLFileUtils::getWriteablePath()
{
	string dir("/data/data/");
	string tmp = ll_getPackageName();
	if (!tmp.empty())
	{
		dir.append(tmp).append("/");

		return dir;
	}
	else
	{
		return "";
	}
}

bool  LLFileUtils::isFileExists(const std::string& fileName)
{
	if (fileName[0] != '/')
	{
		// read from apk
		string fullPath = "assets/" + fileName;
//		pData = zipFile()->getFileData(fullPath, pSize);

		LLJniMethodInfo t;			
		
		if (LLJniHelper::getStaticMethodInfo(t, 
					"com/lidlib/lib/LidlibCommon",
					"isAssetsFileExists",
					"(Ljava/lang/String;)Z"))
		{
			jstring stringArg = t.env->NewStringUTF(fullPath.c_str());
			jboolean result = t.env->CallStaticBooleanMethod(t.classID, t.methodID, stringArg);
			t.env->DeleteLocalRef(stringArg);
			t.env->DeleteLocalRef(t.classID);
			return result;
		}
	}
	else
	{
		FILE *fp = fopen(fileName.c_str(), "rb");
		if(fp == NULL)
			return false;
		fclose(fp);
	}
	return true;
}

unsigned char*  LLFileUtils::getFileData(const std::string& fileName, unsigned long* pSize)
{
	unsigned char * pData = 0;
	string fullPath(fileName);
	if (fullPath.empty())
		return NULL;

	if (fileName[0] != '/')
	{
		// read from apk
		fullPath.insert(0, "assets/");
//		pData = zipFile()->getFileData(fullPath, pSize);

		LLJniMethodInfo t;			
		
		if (LLJniHelper::getStaticMethodInfo(t, 
					"com/lidlib/lib/LidlibCommon",
					"getAssetsFileData",
					"(Ljava/lang/String;)[B;"))
		{
			jstring stringArg = t.env->NewStringUTF(fullPath.c_str());
			jbyteArray pByteArray = (jbyteArray)t.env->CallStaticObjectMethod(t.classID, t.methodID, stringArg);
			t.env->DeleteLocalRef(stringArg);
			t.env->DeleteLocalRef(t.classID);
			if (pByteArray == NULL)
				return NULL;
				
						// Allocates an array of C string.
			*pSize = t.env->functions->GetArrayLength(t.env, pByteArray); 
			jbyte* pDataTemp = new jbyte[*pSize];
			t.env->GetByteArrayRegion(pByteArray, 0, *pSize, pDataTemp);
			pData = (unsigned char *)pDataTemp;
			if (t.env->ExceptionCheck()) 
			{
				delete [] pData;
				return NULL;
			}
		}
	}
	else
	{
		FILE *fp = fopen(fileName.c_str(), "rb");
		if (fp == NULL)
			return NULL;

		fseek(fp,0,SEEK_END);
		*pSize = ftell(fp);

		fseek(fp,0,SEEK_SET);
		pData = new unsigned char[*pSize];
		*pSize = fread(pData,sizeof(unsigned char), *pSize,fp);
		fclose(fp);	
	}

    return pData;
}

std::string  LLFileUtils::fullPathFromRelativePath(const std::string& relativePath)
{
	return relativePath;
}

std::string  LLFileUtils::fullPathFromRelativeFile(const std::string& filename, const std::string& relativeFile)
{
	std::string fullFile = fullPathFromRelativePath(relativeFile);
	std::string ret = fullFile.substr(0, fullFile.find_last_of("/") + 1);
	ret += filename;

	return ret;
}

}; // end namespace lidlib
